上次我們從RichText出發,一步步摸索出了Widget, Element, RenderObject各自的責任,和彼此之間的相互關係。我們也看到RichText有這樣的繼承關係:
class RichText extends MultiChildRenderObjectWidget
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget
而MultiChildRenderObjectWidget產生的MultiChildRenderObjectElement則是這樣的關係:
class MultiChildRenderObjectElement extends RenderObjectElement
abstract class RenderObjectElement extends Element
最後RichText產生的RenderParagraph則是:
class RenderParagraph extends RenderBox
abstract class RenderBox extends RenderObject
光是一個簡單的RichText就牽涉到這麼多的class,它們彼此之間到底是如何對應的?而我們平常在使用的眾多Widget,它們對應的Element和RenderObject又是什麼?讓我們來看看:
這裡我們畫出了Widget, Element, RenderObject各自的繼承關係圖,以及它們之間的(顏色)對應關係。我們可以看到,Widget和Element之間有著明顯的一對一的關係,StatelessWidget產生StatelessElement、SingleChildRenderObjectWidget產生SIngleChildRenderObjectElement...等等。
然而,RenderObject卻沒有和Widget,Element對應的架構。RenderObjectWidget底下的Padding、RichText、RawImage,分別建立了RenderObject這邊的RenderPadding、RenderParagraph、RenderImage,而StatelessWidget和StatefulWidget這邊,我們常見的各種Widget卻沒有對應的RenderObject。
顯然,只有RenderObjectElement才會呼叫RenderObjectWidget去產生RenderObject,而ComponentElement(StatelessElement&StatefulElement並不會呼叫StatelessWidget和StatefulWidget去產生RenderObject,畢竟這兩種Widget本身是由其它Widget複合而成的,不會建立自己的RenderObject。
簡單來說,既然整個Widget Tree是一個由各種Widget複合出來的樹,我們要去渲染它時自然會遞迴地去呼叫StatelessWidget和StatefulWidget的build函數,直到我們遇到某個沒有build函數的RenderObjectWidget,就由它產生其對應的RenderObject。
最後我們以這個簡單的APP為例:
void main() {
runApp(Container(
alignment: Alignment.center,
child: Text(
'Hello, World!',
textDirection: TextDirection.ltr,
),
));
}
我們可以得到這樣的三顆渲染樹:
注意到雖然我們只複合了Container和Text兩個Widget,但當我們在Container上設置各種參數(如alignment)時,它的作用其實是幫我們在我們傳入的child(Text)之上,再去複合各種Widget。最終的四層Widget Tree,產生了對應的四層Element Tree,但只有在遇到RenderObjectWidget時才會去產生RenderObject。
雖然一直逃避作圖到現在,但老實說網路上每一篇關於Flutter渲染樹的文章幾乎都有精美的示意圖,只有我不畫好像有點說不過去。因為美術細胞趨近於零所以又花了不少時間,下次我們終於可以進入真正有趣的部份了:渲染樹的生命週期,也就是從APP啟動,渲染樹建立、更新到回收的整個流程細節,敬請期待!(不保證是下一篇)